using System;
using System.Collections.Generic;
using System.Linq;

namespace CavemanTools.Lists
{
	public static class ListUtils
	{
		/// <summary>
		/// Compares two sequences and returns the added or removed items.
		/// </summary>
		/// <typeparam name="T">Implements IEquatable</typeparam>
		/// <param name="fresh">Recent sequence</param>
		/// <param name="old">Older sequence used as base of comparison</param>
		/// <returns></returns>
		public static IModifiedSet<T> Compare<T>(this IEnumerable<T> fresh, IEnumerable<T> old)	where T:IEquatable<T>
		{
			if (fresh == null) throw new ArgumentNullException("fresh");
			if (old == null) throw new ArgumentNullException("old");
			var mods = new ModifiedSet<T>();
			
			foreach (var item in old)
			{
				if (!fresh.Contains(item)) mods.RemovedItem(item);
			}
			
			foreach (var item in fresh)
			{
				if (!old.Contains(item)) mods.AddedItem(item);
			}
			return mods;
		}

		/// <summary>
		/// Compares two sequences and returns the result.
		/// This special case method is best used when you have identifiable objects that can change their content/value but not their id.
		/// </summary>
		/// <typeparam name="T">Implements IEquatable</typeparam>
		/// <param name="fresh">Recent sequence</param>
		/// <param name="old">Older sequence used as base of comparison</param>
		/// <param name="detectChange">Delegate to determine if the items are identical.
		/// First parameter is new item, second is the item used as base for comparison</param>
		/// <returns></returns>
		public static IModifiedSet<T> WhatChanged<T>(this IEnumerable<T> fresh, IEnumerable<T> old,Func<T,T,bool> detectChange) where T : IEquatable<T>
		{
			if (fresh == null) throw new ArgumentNullException("fresh");
			if (old == null) throw new ArgumentNullException("old");
			if (detectChange == null) throw new ArgumentNullException("detectChange");
			var mods = new ModifiedSet<T>();

			foreach (var item in old)
			{
				if (!fresh.Any(d=> d.Equals(item))) mods.RemovedItem(item);
			}

			foreach (var item in fresh)
			{
				if (!old.Any(d=>d.Equals(item))) mods.AddedItem(item);
				else
				{
					var oldItem = old.First(d => d.Equals(item));	
					if (detectChange(item,oldItem))
					{						
						mods.ModifiedItem(oldItem,item);
					}
				}
			}
			return mods;
		}

		/// <summary>
		/// Checks if a collection is null or empty duh!
		/// </summary>
		/// <typeparam name="T">Type</typeparam>
		/// <param name="items">collection</param>
		/// <returns></returns>
		public static bool IsNullOrEmpty<T>(this IEnumerable<T> items)
		{
			return items == null || !items.Any();
		}
	}
}